/*
 * Copyright (C) 2001-2011 Jacek Sieka, arnetheduck on gmail point com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"
#include "Resource.h"

#include "SpyFrame.h"
#include "SearchFrm.h"
#include "MainFrm.h"

#include "../client/ConnectionManager.h"

int SpyFrame::columnSizes[] = { 305, 70, 90, 120, 20 };
int SpyFrame::columnIndexes[] = { COLUMN_STRING, COLUMN_COUNT, COLUMN_USERS, COLUMN_TIME, COLUMN_SHARE_HIT }; // !SMT!-S
static ResourceManager::Strings columnNames[] = { ResourceManager::SEARCH_STRING, ResourceManager::COUNT, ResourceManager::USERS, ResourceManager::TIME, ResourceManager::SHARED }; // !SMT!-S

LRESULT SpyFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	CreateSimpleStatusBar(ATL_IDS_IDLEMESSAGE, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP);
	ctrlStatus.Attach(m_hWndStatusBar);
	
	ctrlSearches.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
	                    WS_HSCROLL | WS_VSCROLL | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL, WS_EX_CLIENTEDGE, IDC_RESULTS);
	ctrlSearches.SetExtendedListViewStyle(LVS_EX_LABELTIP | LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | (BOOLSETTING(VIEW_GRIDCONTROLS) ? LVS_EX_GRIDLINES : 0));
	ctrlSearches.SetBkColor(WinUtil::bgColor);
	ctrlSearches.SetTextBkColor(WinUtil::bgColor);
	ctrlSearches.SetTextColor(WinUtil::textColor);
	
	ctrlIgnoreTth.Create(ctrlStatus.m_hWnd, rcDefault, CTSTRING(IGNORE_TTH_SEARCHES), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
	ctrlIgnoreTth.SetButtonStyle(BS_AUTOCHECKBOX, false);
	ctrlIgnoreTth.SetFont(WinUtil::systemFont);
	ctrlIgnoreTth.SetCheck(ignoreTth);
	ignoreTthContainer.SubclassWindow(ctrlIgnoreTth.m_hWnd);
	
	WinUtil::splitTokens(columnIndexes, SETTING(SPYFRAME_ORDER), COLUMN_LAST);
	WinUtil::splitTokens(columnSizes, SETTING(SPYFRAME_WIDTHS), COLUMN_LAST);
	for (int j = 0; j < COLUMN_LAST; j++)
	{
		int fmt = (j == COLUMN_COUNT) ? LVCFMT_RIGHT : LVCFMT_LEFT;
		ctrlSearches.InsertColumn(j, CTSTRING_I(columnNames[j]), fmt,
		                          (j != COLUMN_USERS || BOOLSETTING(SHOW_SEEKERS_IN_SPY_FRAME)) ? columnSizes[j] : 0// [+] IRainman
		                          , j);
	}
	
	ctrlSearches.setSort(COLUMN_COUNT, ExListViewCtrl::SORT_INT, false);
	//ctrlSearches.setVisible(SETTING(SPYFRAME_VISIBLE)); // !SMT!-UI
	
	ShareManager::getInstance()->setHits(0);

#ifdef _BIG_BROTHER_MODE
	//[!]IRainman refactoring SpyFrame
	if (BOOLSETTING(OPEN_SEARCH_SPY))
	{
	    try
	    {
	        m_log = new File(Util::getConfigPath() + "SpyLog.txt", File::WRITE, File::OPEN );
	        m_log->setEndPos(0);
	        if (m_log->getPos() == 0)
	            m_log->write("\xef\xbb\xbf");
	    }
	    catch (const FileException&)
	    {
	    }
	}
#endif
	//[~]IRainman refactoring SpyFrame
	bHandled = FALSE;
	return 1;
}

LRESULT SpyFrame::onClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	if (!closed)
	{
#ifdef _BIG_BROTHER_MODE
		if (m_log)
			PostMessage(WM_SPEAKER, SAVE_LOG, (LPARAM)NULL);
#endif
		ClientManager::getInstance()->removeListener(this);
		TimerManager::getInstance()->removeListener(this);
		SettingsManager::getInstance()->removeListener(this);
		bHandled = TRUE;
		closed = true;
		PostMessage(WM_CLOSE);
		return 0;
	}
	else
	{
		WinUtil::saveHeaderOrder(ctrlSearches, SettingsManager::SPYFRAME_ORDER, SettingsManager::SPYFRAME_WIDTHS, COLUMN_LAST, columnIndexes, columnSizes);
		//ctrlSearches.saveHeaderOrder(SettingsManager::SPYFRAME_ORDER, SettingsManager::SPYFRAME_WIDTHS, SettingsManager::SPYFRAME_VISIBLE); // !SMT!-UI
		if (ignoreTth != BOOLSETTING(SPY_FRAME_IGNORE_TTH_SEARCHES))
			SET_SETTING(SPY_FRAME_IGNORE_TTH_SEARCHES, ignoreTth);
#ifdef _BIG_BROTHER_MODE			
		if (m_log)
			delete m_log;
#endif
		WinUtil::setButtonPressed(IDC_SEARCH_SPY, false);
		bHandled = FALSE;
		return 0;
	}
}

LRESULT SpyFrame::onColumnClickResults(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
	NMLISTVIEW* l = (NMLISTVIEW*)pnmh;
	if (l->iSubItem == ctrlSearches.getSortColumn())
	{
		if (!ctrlSearches.isAscending())
			ctrlSearches.setSort(-1, ctrlSearches.getSortType());
		else
			ctrlSearches.setSortDirection(false);
	}
	else
	{
		if (l->iSubItem == COLUMN_COUNT)
		{
			ctrlSearches.setSort(l->iSubItem, ExListViewCtrl::SORT_INT);
		}
		else
		{
			ctrlSearches.setSort(l->iSubItem, ExListViewCtrl::SORT_STRING_NOCASE);
		}
	}
	return 0;
}

void SpyFrame::UpdateLayout(BOOL bResizeBars /* = TRUE */)
{
	RECT rect;
	GetClientRect(&rect);
	// position bars and offset their dimensions
	UpdateBarsPosition(rect, bResizeBars);
	
	if (ctrlStatus.IsWindow())
	{
		CRect sr;
		int w[6];
		ctrlStatus.GetClientRect(sr);
		
		int tmp = (sr.Width()) > 616 ? 516 : ((sr.Width() > 116) ? sr.Width() - 100 : 16);
		
		w[0] = 150;
		w[1] = sr.right - tmp - 150;
		w[2] = w[1] + (tmp - 16) * 1 / 4;
		w[3] = w[1] + (tmp - 16) * 2 / 4;
		w[4] = w[1] + (tmp - 16) * 3 / 4;
		w[5] = w[1] + (tmp - 16) * 4 / 4;
		
		ctrlStatus.SetParts(6, w);
		
		ctrlStatus.GetRect(0, sr);
		ctrlIgnoreTth.MoveWindow(sr);
	}
	
	ctrlSearches.MoveWindow(&rect);
}

LRESULT SpyFrame::onSpeaker(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
	if (wParam == SEARCH)
	{
		SMTSearchInfo* x = (SMTSearchInfo*)lParam;
#ifdef _BIG_BROTHER_MODE
		if (m_log)
		{
			Lock l(cs);
			m_txt += m_CurrentTime + "\t" +
			         x->seeker + "\t" +
			         x->s + "\r\n";
		}
#endif
		
		const SearchIter it2 = searches.find(x->s);
		if (it2 == searches.end())
		{
			searches[x->s].i = 1;
		}
		
		string temp;
		if (BOOLSETTING(SHOW_SEEKERS_IN_SPY_FRAME))// [+] IRainman
		{
			if (::strncmp(x->seeker.c_str(), "Hub:", 4))
			{
				const string::size_type l_twopt = x->seeker.find(':');
				if (l_twopt != string::npos)
				{
					x->seeker = x->seeker.substr(0, l_twopt);
					const UserPtr u = ClientManager::getInstance()->getUserByIp(x->seeker);
					if (u)
					{
						x->seeker = u->getFirstNick();
					}
				}
			}
			
			size_t k;
			for (k = 0; k < NUM_SEEKERS; ++k)
				if (x->seeker == (searches[x->s].seekers)[k])
					break;          //that user's searching for file already noted
					
			{
				Lock l(cs);
				if (k == NUM_SEEKERS)           //loop terminated normally
					searches[x->s].AddSeeker(x->seeker);
			}
			
			for (k = 0; k < NUM_SEEKERS; ++k)
				temp += (searches[x->s].seekers)[k] + "  ";
		}
		
		total++;
		
		perSecond[cur]++;

		// !SMT!-S
		string hit;
		if (x->re == ClientManagerListener::SEARCH_PARTIAL_HIT)
			hit = "*";
		else if (x->re == ClientManagerListener::SEARCH_HIT)
			hit = "+";
			
		tstring l_search;
		l_search = Text::toT(x->s, l_search);
		const int j = ctrlSearches.find(l_search);
		if (j == -1)
		{
			TStringList a;
			a.push_back(l_search);
			a.push_back(Text::toT(Util::toString(1)));
			a.push_back(Text::toT(temp));
			a.push_back(Text::toT(m_CurrentTime));
			a.push_back(Text::toT(hit));
			ctrlSearches.insert(a, 0, x->re);// !SMT!-S
			int l_Count = ctrlSearches.GetItemCount();
			if (l_Count > 500)
			{
				ctrlSearches.DeleteItem(--l_Count);
			}
		}
		else
		{
			TCHAR tmp[32];
			ctrlSearches.GetItemText(j, COLUMN_COUNT, tmp, 32);
			ctrlSearches.SetItemText(j, COLUMN_COUNT, Util::toStringW(Util::toInt(Text::fromT(tmp)) + 1).c_str());
			ctrlSearches.SetItemText(j, COLUMN_USERS, Text::toT(temp).c_str());
			ctrlSearches.SetItemText(j, COLUMN_TIME, Text::toT(m_CurrentTime).c_str());
			ctrlSearches.SetItemText(j, COLUMN_SHARE_HIT, Text::toT(hit).c_str()); // !SMT!-S
			ctrlSearches.SetItem(j, COLUMN_SHARE_HIT, LVIF_PARAM, NULL, 0, 0, 0, x->re); // !SMT!-S
			if (ctrlSearches.getSortColumn() == COLUMN_COUNT ||
			        ctrlSearches.getSortColumn() == COLUMN_TIME
			   )
				ctrlSearches.resort();
		}
		delete x;
		
		//Bolded activity in SpyFrame
		if (BOOLSETTING(BOLD_SEARCH))
		{
			setDirty();
		}		
		
	}
	else if (wParam == TICK_AVG)
	{
		float* x = (float*)lParam;
		ctrlStatus.SetText(2, (TSTRING(TOTAL) + _T(" ") + Util::toStringW(total)).c_str());
		ctrlStatus.SetText(3, (TSTRING(AVERAGE) + _T(" ") + Util::toStringW(*x)).c_str());
		ctrlStatus.SetText(4, (TSTRING(HITS) + _T(" ") + Util::toStringW((size_t)(ShareManager::getInstance()->getHits()))).c_str());
		const double ratio = total > 0 ? static_cast<double>(ShareManager::getInstance()->getHits()) / static_cast<double>(total) : 0.0;
		ctrlStatus.SetText(5, (TSTRING(HIT_RATIO) + _T(" ") + Util::toStringW(ratio)).c_str());
		delete x;
	}
#ifdef _BIG_BROTHER_MODE
	else if (wParam == SAVE_LOG)
	{
		Lock l(cs);
		try
		{
			m_log->setEndPos(0);
			m_log->write(m_txt);
		}
		catch (const FileException&)
		{
		}
		m_txt.clear();
	}
#endif
	
	return 0;
}

LRESULT SpyFrame::onContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	if (reinterpret_cast<HWND>(wParam) == ctrlSearches && ctrlSearches.GetSelectedCount() == 1)
	{
		POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
		
		if (pt.x == -1 && pt.y == -1)
		{
			WinUtil::getContextMenuPos(ctrlSearches, pt);
		}
		
		int i = ctrlSearches.GetNextItem(-1, LVNI_SELECTED);
		
		CMenu mnu;
		mnu.CreatePopupMenu();
		mnu.AppendMenu(MF_STRING, IDC_SEARCH, CTSTRING(SEARCH));
		
		searchString.resize(256);
		ctrlSearches.GetItemText(i, COLUMN_STRING, &searchString[0], searchString.size());
		
		mnu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, m_hWnd);
		
		return TRUE;
	}
	
	bHandled = FALSE;
	return FALSE;
}

LRESULT SpyFrame::onSearch(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	if (strnicmp(searchString.c_str(), _T("TTH:"), 4) == 0)
		SearchFrame::openWindow(searchString.substr(4), 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
	else
		SearchFrame::openWindow(searchString);
	return 0;
}

void SpyFrame::on(ClientManagerListener::IncomingSearch, const string& user, const string& s, ClientManagerListener::SearchReply re) noexcept
{
	if (ignoreTth && isTTHBase64(s))
		return;
		
	SMTSearchInfo *x = new SMTSearchInfo(user, s, re);
	string::size_type i = 0;
	while ((i = (x->s).find('$')) != string::npos)
	{
		(x->s)[i] = ' ';
	}
	PostMessage(WM_SPEAKER, SEARCH, (LPARAM)x);
}

void SpyFrame::on(TimerManagerListener::Second, uint64_t) noexcept
{
	float* f = new float(0.0);
	m_CurrentTime = Util::formatTime("%d-%m-%Y %H:%M:%S", GET_TIME());//[+]IRainman refactoring SpyFrame
	if (MainFrame::getAppMinimized())
	{
		if (cur++ >= AVG_TIME)
			cur = 0;

		perSecond[cur] = 0;
	}
	else
	{
		for (size_t i = 0; i < AVG_TIME; ++i)
			(*f) += (float)perSecond[i];

		(*f) /= AVG_TIME;

		if (cur++ >= AVG_TIME)
			cur = 0;

		perSecond[cur] = 0;
		PostMessage(WM_SPEAKER, TICK_AVG, (LPARAM)f);
	}
#ifdef _BIG_BROTHER_MODE
	if (m_log && ++m_tick > 10)
	{
		PostMessage(WM_SPEAKER, SAVE_LOG, (LPARAM)NULL);
		m_tick = 0;
	}
#endif
}

void SpyFrame::on(SettingsManagerListener::Save, SimpleXML& /*xml*/) noexcept
{
	bool refresh = false;
	if (ctrlSearches.GetBkColor() != WinUtil::bgColor)
	{
		ctrlSearches.SetBkColor(WinUtil::bgColor);
		ctrlSearches.SetTextBkColor(WinUtil::bgColor);
		refresh = true;
	}
	if (ctrlSearches.GetTextColor() != WinUtil::textColor)
	{
		ctrlSearches.SetTextColor(WinUtil::textColor);
		refresh = true;
	}
	if (refresh == true)
	{
		RedrawWindow(NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
	}
}

// !SMT!-S
LRESULT SpyFrame::onCustomDraw(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
	NMLVCUSTOMDRAW* plvcd = reinterpret_cast<NMLVCUSTOMDRAW*>(pnmh);
	
	if (CDDS_PREPAINT == plvcd->nmcd.dwDrawStage)
		return CDRF_NOTIFYITEMDRAW;
		
	if (CDDS_ITEMPREPAINT == plvcd->nmcd.dwDrawStage)
	{
		ClientManagerListener::SearchReply re = (ClientManagerListener::SearchReply)(plvcd->nmcd.lItemlParam);
		
		//check if the file or dir is a dupe, then use the dupesetting color
		if (re == ClientManagerListener::SEARCH_HIT)
			plvcd->clrTextBk = SETTING(DUPE_COLOR);
		else if (re == ClientManagerListener::SEARCH_PARTIAL_HIT)
			//if it's a partial hit, try to use some simple blending
			plvcd->clrTextBk = WinUtil::blendColors(SETTING(DUPE_COLOR), SETTING(BACKGROUND_COLOR));
	}
	return CDRF_DODEFAULT;
}

/**
 * @file
 * $Id: SpyFrame.cpp,v 1.38 2006/10/13 20:04:32 bigmuscle Exp $
 */
